package wiki.kaizen.cloud.shiro.stateless.util;

import io.jsonwebtoken.Claims;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestClientException;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import wiki.kaizen.cloud.shiro.stateless.exception.ErrorTypeEnum;
import wiki.kaizen.cloud.shiro.stateless.properties.SecurityProperties;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.UUID;

/**
 * 无状态安全工具类
 * @author  xeepoo
 * */

public class ISecurityUtil {
    /*
     * 加密算法
     * */
    private static String hashAlgorithmName;

    /**
     *循环次数
     */
    private static Short hashIterations;

    private static RedisUtil.Cache<String> refreshTokenCache;
    //默认值
    static {
        hashAlgorithmName = "MD5";
        hashIterations = 4;
        refreshTokenCache = RedisUtil.getCache(
            SecurityProperties.Prefix.token
        );
    }

    public static void setSecurityProperties(SecurityProperties properties){
        hashAlgorithmName = properties.getAccount().getHashAlgorithmName();
        hashIterations = properties.getAccount().getHashIterations();

    }


    /**
     * 登录操作
     * @param userCode  用户唯一属性 如:id 或 userName
     * */
    public static JWTUtil.Token login(String userCode){
        JWTUtil.Token token = JWTUtil.createJWT(userCode);
        refreshTokenCache.set(
            userCode,
            token.getRefreshToken().getToken() ,
            SecurityProperties.refreshExpireTime
        );
        return  token;
    }

    /**
    * 退出登录/登出
    * */
    public static void logout(){
        String userCode = String.valueOf(
            SecurityUtils.getSubject().getPrincipal()
        );
        if(StringUtils.isEmpty(userCode)){
            throw new RestClientException("未登录");
        }
        refreshTokenCache.del(userCode);
    }
    /**
    * 校验tuken有效性
    *  为空 验证通过 // 不为空 则验证不通过
    * */
    public static void check(String token){
        /*token*/
        if (StringUtils.isEmpty(token)){//未登录
            throw ErrorTypeEnum.NOT_LOGIN.getException();
        }
        Claims claims;
        try{
            claims = JWTUtil.parseJWT(token);
        }catch (Exception ignore){
            throw ErrorTypeEnum.INVALID_TOKEN.getException();
        }

        if ( claims.getExpiration().before(new Date()) ){
            throw ErrorTypeEnum.EXPIRED_TOKEN.getException();
        }

        String userCode = claims.getSubject();
        if (StringUtils.isEmpty(userCode)){
            throw ErrorTypeEnum.INVALID_TOKEN.getException();
        }
        /*refresh token*/

        String cacheRefreshToken = refreshTokenCache.get(userCode);
        if (StringUtils.isEmpty(cacheRefreshToken)){
            throw ErrorTypeEnum.LOGIN_MISSED.getException();
        }

        Claims refreshToken;
        try{
            refreshToken = JWTUtil.parseJWT(cacheRefreshToken);
        }catch (Exception ignore){
            throw ErrorTypeEnum.INVALID_TOKEN.getException();
        }

        if ( !claims.getIssuedAt().equals(refreshToken.getIssuedAt()) ){
            throw ErrorTypeEnum.REPEAT_LOGIN.getException();
        }

        if (
            ! claims.getId().equals(claims.getAudience()+refreshToken.getId()) || !userCode.equals(refreshToken.getSubject())){
            throw ErrorTypeEnum.INVALID_TOKEN.getException();
        }
    }

    /**
     * 密码 加盐 加密
     * */
    public static String doHash(String password,String salt){
        return new SimpleHash(hashAlgorithmName,password,salt,hashIterations).toString();
    }

    /**
     * 获取当前请求的 requestAttributes
     * */
    public static  ServletRequestAttributes getRequestAttribute(){
         ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
         if (requestAttributes == null){
             throw new RestClientException("无法正确获取Request");
         }
         return requestAttributes;
     }

     /*
     *获取当前请求的 request
     * */
     public static HttpServletRequest request(){
        return  getRequestAttribute().getRequest();
     }

    /*
     *获取当前请求的 response
     * */
    public static HttpServletResponse response(){
        return  getRequestAttribute().getResponse();
    }

    /*
    * 从 reqest中取token
    * */
    public static String token(){
        return request().getHeader(SecurityProperties.tokenHeaderName);
    }


    /**
    * 获取uid
    * */
    public static String uid(){
        final  String UID_KEY = "uid";
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null){
            throw new RestClientException("无法正确获取Request");
        }
        HttpServletRequest request = requestAttributes.getRequest();

        String token = request.getHeader(SecurityProperties.tokenHeaderName);

        String uid = null;

        if(!StringUtils.isEmpty(token)){
            try{
                Claims claims = JWTUtil.parseJWT(token);
                uid = claims.getId().replaceFirst(claims.getAudience(),"");
            }catch (Exception ignore){
                uid = null;
            }
        }

        boolean empty = StringUtils.isEmpty(uid);

        if (empty){
            uid = request.getHeader(UID_KEY);
            if (StringUtils.isEmpty(uid)){
                HttpServletResponse response = requestAttributes.getResponse();
                if (response == null){
                    throw new RestClientException("设置uid时发生异常");
                }
                uid = response.getHeader(UID_KEY);
                if (
                    StringUtils.isEmpty(uid)
                ){
                    uid = generateUID();
                    response.addHeader(UID_KEY,uid);
                }
            }

        }
        return uid;
    }

    /**
     * uid 生成方法
     * */
    public static String generateUID(){
        return  UUID.randomUUID().toString().replace("-","");
    }

}
